package character;

import java.io.FileNotFoundException;
import java.io.PrintStream;
import java.util.Formatter;
import java.util.Locale;

import org.jgap.Chromosome;
import org.jgap.Configuration;
import org.jgap.Gene;
import org.jgap.Genotype;
import org.jgap.IChromosome;
import org.jgap.InvalidConfigurationException;
import org.jgap.Population;
import org.jgap.impl.IntegerGene;

import configuration.LeveledConfiguration;
import configuration.ProfileMutation;

public class Profiler {

	/**
	 * @param args
	 * @throws InvalidConfigurationException
	 * @throws FileNotFoundException
	 */
	public static void main(String[] args) throws InvalidConfigurationException, FileNotFoundException {
		int populationSize=10;
		PrintStream out;
		
		Druid aDruid = new Druid();
		out = new PrintStream("outputs/druid.out");
		runProfilerForClass(populationSize, aDruid, out);
		out.close();

		Hunter aHunter = new Hunter();
		out = new PrintStream("outputs/hunter.out");
		runProfilerForClass(populationSize, aHunter, out);
		out.close();

		Mage aMage = new Mage();
		out = new PrintStream("outputs/mage.out");
		runProfilerForClass(populationSize, aMage, out);
		out.close();
		
		Priest aPriest = new Priest();
		out = new PrintStream("outputs/priest.out");
		runProfilerForClass(populationSize, aPriest, out);
		out.close();
		
		Paladin aPaladin = new Paladin();
		out = new PrintStream("outputs/paladin.out");
		runProfilerForClass(populationSize, aPaladin, out);
		out.close();

		Rogue aRogue = new Rogue();
		out = new PrintStream("outputs/rogue.out");
		runProfilerForClass(populationSize, aRogue, out);
		out.close();

		Shaman aShaman = new Shaman();
		out = new PrintStream("outputs/shaman.out");
		runProfilerForClass(populationSize, aShaman, out);
		out.close();

		Warlock aWarlock = new Warlock();
		out = new PrintStream("outputs/warlock.out");
		runProfilerForClass(populationSize, aWarlock, out);
		out.close();

		Warrior aWarrior = new Warrior();
		out = new PrintStream("outputs/warrior.out");
		runProfilerForClass(populationSize, aWarrior, out);
		out.close();
		
	}

	/**
	 * @param populationSize
	 * @param aProfile
	 * @param out 
	 * @throws InvalidConfigurationException
	 */
	private static void runProfilerForClass(int populationSize, Profile aProfile, PrintStream out)
			throws InvalidConfigurationException {
		Configuration conf = new LeveledConfiguration();
		conf.addGeneticOperator(new ProfileMutation());
		conf.setFitnessFunction(aProfile.getProfileFitness());
		conf.setPopulationSize(populationSize);
		/* ***************************************************************** */
		Chromosome sampleChromosome = getSampleChromosome(conf);
		conf.setSampleChromosome( sampleChromosome );
		/* ***************************************************************** */
		Genotype population = Genotype.randomInitialGenotype( conf );
		
		setPopulationToBase(population);
		
		out.println("Population:");
		printPopulation(population, out);
		
		out.println("Fittest individual:");
		printFittestChromosome(population, out);
		
		for(int k=0; k<69; k++){
			population.evolve();
			
			out.println("/* ***************************************************************** */");			
			
			out.println("Population:");
			printPopulation(population, out);
			
			out.println("Fittest individual:");
			printFittestChromosome(population, out);
		}

		IChromosome fittest = population.getFittestChromosome();
		Integer strength = (Integer) fittest.getGene(0).getAllele();
		Integer agility =  (Integer) fittest.getGene(1).getAllele();
		Integer stamina =  (Integer) fittest.getGene(2).getAllele();
		Integer intellect =  (Integer) fittest.getGene(3).getAllele();
		Integer spirit =  (Integer) fittest.getGene(4).getAllele();
		
		aProfile.setStrength(strength);
		aProfile.setAgility(agility);
		aProfile.setStamina(stamina);
		aProfile.setIntellect(intellect);
		aProfile.setSpirit(spirit);
		
		printProfileSecondarySkills(aProfile, out);

	}

	/**
	 * @param aProfile
	 * @param out
	 */
	private static void printProfileSecondarySkills(Profile aProfile,
			PrintStream out) {
		Formatter formatter;
		
		out.println("Secondary skills values @ lvl 70");
		formatter = new Formatter();
		out.println(formatter.format("%1.4f", aProfile.getAttackPower()) );
		formatter = new Formatter();
		out.println(formatter.format("%1.4f", aProfile.getArmor()) ); 
		formatter = new Formatter();
		out.println(formatter.format("%1.4f", aProfile.getSpeed()) );
		formatter = new Formatter();
		out.println(formatter.format("%1.4f", aProfile.getResilience()) ); 
		formatter = new Formatter();
		out.println(formatter.format("%1.4f", aProfile.getDodgeRating()) ); 
		formatter = new Formatter();
		out.println(formatter.format("%1.4f", aProfile.getMana()) );
		formatter = new Formatter();
		out.println(formatter.format("%1.4f", aProfile.getHealth()) );
		formatter = new Formatter();
		out.println(formatter.format("%1.4f", aProfile.getSpellPenetration()) );
		formatter = new Formatter();
		out.println(formatter.format("%1.4f", aProfile.getHaste()) );
		formatter = new Formatter();
		out.println(formatter.format("%1.4f", aProfile.getHealthRegeneration()) );
		formatter = new Formatter();
		out.println(formatter.format("%1.4f", aProfile.getManaRegeneration()) );
	}
	
	/**
	 * @param aProfile
	 * @param out
	 */
	private static void printProfileSecondarySkillsWithLabels (Profile aProfile,
			PrintStream out) {
		Formatter formatter;
		
		out.println("Secondary skills values @ lvl 70");
		formatter = new Formatter();
		out.println("AttackPower\t" + formatter.format("%10.4f", aProfile.getAttackPower()) );
		formatter = new Formatter();
		out.println("Armor\t" + formatter.format("%10.4f", aProfile.getArmor()) ); 
		formatter = new Formatter();
		out.println("Speed\t" + formatter.format("%10.4f", aProfile.getSpeed()) );
		formatter = new Formatter();
		out.println("Resilience\t" + formatter.format("%10.4f", aProfile.getResilience()) ); 
		formatter = new Formatter();
		out.println("DodgeRating\t" + formatter.format("%10.4f", aProfile.getDodgeRating()) ); 
		formatter = new Formatter();
		out.println("Mana\t" + formatter.format("%10.4f", aProfile.getMana()) );
		formatter = new Formatter();
		out.println("Health\t" + formatter.format("%10.4f", aProfile.getHealth()) );
		formatter = new Formatter();
		out.println("Penetration\t" + formatter.format("%10.4f", aProfile.getSpellPenetration()) );
		formatter = new Formatter();
		out.println("Haste\t" + formatter.format("%10.4f", aProfile.getHaste()) );
		formatter = new Formatter();
		out.println("HealthRegeneration\t" + formatter.format("%10.4f", aProfile.getHealthRegeneration()) );
		formatter = new Formatter();
		out.println("ManaRegeneration\t" + formatter.format("%10.4f", aProfile.getManaRegeneration()) );
	}

	/**
	 * @param population
	 */
	private static void printFittestChromosome(Genotype population, PrintStream out) {
		IChromosome bestSolutionSoFar = population.getFittestChromosome();
		for(int j = 0; j<5; j++){
			out.print(bestSolutionSoFar.getGene(j).getAllele());
			out.print(" | ");
		}
		out.print(bestSolutionSoFar.getFitnessValue());
		out.println("");
	}

	/**
	 * @param populationSize
	 * @param population
	 */
	private static void printPopulation(Genotype population, PrintStream out) {
		int populationSize = population.getPopulation().size();
		for(int i=0; i<populationSize; i++){
			IChromosome individual = population.getPopulation().getChromosome(i);
			for(int j = 0; j<5; j++){
				out.print(individual.getGene(j).getAllele());
				out.print(" | ");
			}
			out.print(individual.getFitnessValue());
			out.println("");
		}
	}

	/**
	 * @param conf
	 * @return
	 * @throws InvalidConfigurationException
	 */
	private static Chromosome getSampleChromosome(Configuration conf)
			throws InvalidConfigurationException {
		Gene[] sampleGenes = new Gene[ 5 ];
		sampleGenes[0] = new IntegerGene(conf, 1, 5000);
		sampleGenes[1] = new IntegerGene(conf, 1, 5000);
		sampleGenes[2] = new IntegerGene(conf, 1, 5000);
		sampleGenes[3] = new IntegerGene(conf, 1, 5000);
		sampleGenes[4] = new IntegerGene(conf, 1, 5000);
		Chromosome sampleChromosome = new Chromosome(conf, sampleGenes );
		return sampleChromosome;
	}

	/**
	 * @param population
	 */
	private static void setPopulationToBase(Genotype population) {
		Population myPop = population.getPopulation();
		for(int i=0; i<myPop.size(); i++){
			IChromosome ind = myPop.getChromosome(i);
			for(int j=0; j<5; j++) {
				ind.getGene(j).setAllele(new Integer(1));
			}
		}
	}

}
